package com.cxria.script.upload;

import com.cxria.gaiamount.commons.cloud.sqs.MessageHandler;
import com.cxria.gaiamount.commons.cloud.sqs.aliyun.AliyunSQSApi;
import com.cxria.gaiamount.commons.cloud.sqs.aws.AmazonSQSApi;
import com.cxria.script.upload.dao.IUploadDao
import com.cxria.script.upload.dao.impl.UploadDao;
import com.cxria.script.upload.domain.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.groovy.parser.antlr4.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.transaction.Transactional;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

class ReceiveMessageTask {

    Logger logger = LoggerFactory.getLogger(this.getClass());
    ObjectMapper objectMapper = new ObjectMapper();
    final IUploadDao uploadDao;

    ReceiveMessageTask() {
        this.uploadDao = new UploadDao()
    }

    int receiveMessage(String type, String endpoint, String queue) {
        MessageHandler messageHandler;
        if ("aws" == type) {
            messageHandler = new AmazonSQSApi(endpoint, queue);
        } else {
            messageHandler = new AliyunSQSApi(endpoint, queue);
        }
        Map<String, String> messages = messageHandler.receiveMessage();
        messages.forEach{receiptHandle, message ->
            if (analyze(message, type)) {
                messageHandler.deleteMessage(receiptHandle);
            }
        }
        return messages.size();
    }

    @Transactional
    boolean analyze(String messageBody, String serverType) {
        try {
            JsonNode root = objectMapper.readTree(messageBody);
            boolean success = root.get("success").asBoolean();
            int code = root.get("code").asInt();
            String message = root.get("message").asText();
            String action = root.get("action").asText();
            JsonNode result = root.get("result");
            JsonNode jobJson = getJobJson(action, result);
            String logId = jobJson.get("log").asText();
            String jobId = jobJson.get("jobId").asText();
            String groupId = jobJson.get("groupId").asText();
            Job job = uploadDao.getJob(jobId, groupId, action);
            if (job != null) {
                return true;
            }
            job = new Job();
            job.setServerType(serverType);
            job.setSuccess(success);
            job.setCode(code);
            job.setType(action);
            job.setLogId(logId);
            job.setMessage(message);
            job.setResult(result.asText());
            job.setJobId(jobId);
            job.setGroupId(groupId);
            long videoId = jobJson.get("videoId").asLong();
            Video video = uploadDao.get(Video.class, videoId);
            if (video == null) {
                logger.error("video : " + videoId + " not exist, logId : " + logId);
                return false;
            }
            if (saveJob(job, video)) {
                if (success) {
                    switch (action) {
                        case "STANDARD":
                            video.setState(2);
                            break;
                        case "PROBE":
                            probe(job, result);
                            break;
                        case "SCREENSHOT":
                            screenshot(job, result);
                            checkVideoState(job, video);
                            break;
                        case "HLS_AVC":
                            hls(job, result);
                            checkVideoState(job, video);
                            break;
                        case "HLS_HEVC":
                            hls(job, result);
                            checkVideoState(job, video);
                            break;
                        case "HLS_VP9":
                            hls(job, result);
                            checkVideoState(job, video);
                            break;
                        case "TRANSCODE":
                            transcode(job, result);
                            break;
                        default:
                            logger.error("Action not case, messageBody : " + messageBody);
                            return false;
                    }
                } else {
                    //设置处理任务失败
                    if (!action.startsWith("TRANSCODE")) {
                        video.setState(6);
                    }
                }
            }
        } catch (IOException e) {
            logger.error(e.getLocalizedMessage());
            return false;
        }
        return true;
    }

    JsonNode getJobJson(String action, JsonNode result) {
        if ("STANDARD".equals(action)) {
            return result;
        }
        return result.get("job");
    }

    boolean saveJob(Job job, Video video) {
        if (!StringUtils.isEmpty(video.getCurrentGroupId()) && job.getGroupId().compareTo(video.getCurrentGroupId()) < 0) {
            return false;
        }
        if (StringUtils.isEmpty(video.getCurrentGroupId()) || job.getGroupId().compareTo(video.getCurrentGroupId()) > 0) {
            deleteOldResources(video.getCurrentGroupId());
            video.setCurrentGroupId(job.getGroupId());
        }
        job.setVideo(video);
        uploadDao.save(job);
        return true;
    }

    void probe(Job job, JsonNode result) {
        JsonNode videoStreamInfo = result.get("videoStreamInfo");
        VideoInfo videoInfo = uploadDao.get(VideoInfo.class, job.getVideo().getId());
        if (videoInfo == null) {
            videoInfo = new VideoInfo();
        }
        Iterator<JsonNode> resolutionNames = result.get("resolutionInfo").iterator();
        double resolution = resolutionNames.next().get("k").asDouble();
        videoInfo.setVideo(job.getVideo());
        videoInfo.setFps(videoStreamInfo.get("fps").asDouble());
        videoInfo.setDuration(videoStreamInfo.get("duration").asDouble());
        videoInfo.setResolution(resolution);
        videoInfo.setProfile(videoStreamInfo.get("profile").asText());
        videoInfo.setLevel(videoStreamInfo.get("level").asText());
        videoInfo.setWidth(videoStreamInfo.get("width").asInt());
        videoInfo.setHeight(videoStreamInfo.get("height").asInt());
        videoInfo.setColorSpace(videoStreamInfo.get("color_space").asText());
        videoInfo.setPixelFormat(videoStreamInfo.get("pix_fmt").asText());
        videoInfo.setDar(videoStreamInfo.get("display_aspect_ratio").asText());
        videoInfo.setBits(videoStreamInfo.get("bits_per_raw_sample").asInt());
        videoInfo.setSar(videoStreamInfo.get("sample_aspect_ratio").asText());
        videoInfo.setStartTime(videoStreamInfo.get("start_time").asDouble());
        videoInfo.setBitRate(videoStreamInfo.get("bit_rate").asInt());
        videoInfo.setFrames(videoStreamInfo.get("nb_frames").asInt());
        videoInfo.setFrameRate(videoStreamInfo.get("avg_frame_rate").asText());
        videoInfo.setCodecLongName(videoStreamInfo.get("codec_long_name").asText());
        videoInfo.setCodecName(videoStreamInfo.get("codec_name").asText());
        JsonNode sourceDynamicRange = result.get("transcodeInfo").get("sourceDynamicRange");
        if (!sourceDynamicRange.isNull()) {
            videoInfo.setSourceDynamicRange(sourceDynamicRange.asText());
        }

        JsonNode audioStreamInfo = result.get("audioStreamInfo");
        AudioInfo audioInfo = uploadDao.get(AudioInfo.class, job.getVideo().getId());
        if (audioInfo == null) {
            audioInfo = new AudioInfo();
        }
        audioInfo.setVideo(job.getVideo());
        audioInfo.setDuration(audioStreamInfo.get("duration").asDouble());
        audioInfo.setProfile(audioStreamInfo.get("profile").asText());
        audioInfo.setChannels(audioStreamInfo.get("channels").asInt());
        audioInfo.setSampleRate(audioStreamInfo.get("sample_rate").asInt());
        audioInfo.setChannelLayout(audioStreamInfo.get("channel_layout").asText());
        audioInfo.setSampleFormat(audioStreamInfo.get("sample_fmt").asText());
        audioInfo.setStartTime(audioStreamInfo.get("start_time").asDouble());
        audioInfo.setBitRate(audioStreamInfo.get("bit_rate").asInt());
        audioInfo.setFrames(audioStreamInfo.get("nb_frames").asInt());
        audioInfo.setCodecLongName(audioStreamInfo.get("codec_long_name").asText());
        audioInfo.setCodecName(audioStreamInfo.get("codec_name").asText());

        JsonNode formatInfoJson = result.get("formatInfo");
        JsonNode cropResult = result.get("cropResult");
        FormatInfo formatInfo = uploadDao.get(FormatInfo.class, job.getVideo().getId());
        if (formatInfo == null) {
            formatInfo = new FormatInfo();
        }
        formatInfo.setVideo(job.getVideo());
        formatInfo.setSize(formatInfoJson.get("size").asInt());
        formatInfo.setDuration(formatInfoJson.get("duration").asDouble());
        String[] fileNames = formatInfoJson.get("filename").asText().split("\\.");
        formatInfo.setExtension(fileNames[fileNames.length - 1]);
        formatInfo.setFormatName(formatInfoJson.get("format_name").asText());
        formatInfo.setStartTime(formatInfoJson.get("start_time").asDouble());
        formatInfo.setBitRate(formatInfoJson.get("bit_rate").asInt());
        formatInfo.setStreams(formatInfoJson.get("nb_streams").asInt());
        formatInfo.setFormatLongName(formatInfoJson.get("format_long_name").asText());
        formatInfo.setCropWidth(cropResult.get("width").asInt());
        formatInfo.setCropHeight(cropResult.get("height").asInt());
        formatInfo.setCropX(cropResult.get("x").asInt());
        formatInfo.setCropY(cropResult.get("y").asInt());
        uploadDao.saveOrUpdate(videoInfo, audioInfo, formatInfo);
    }

    void screenshot(Job job, JsonNode result) {
        JsonNode filePaths = result.get("filePaths");
        filePaths.forEach{uri ->
            String uriStr = uri.asText();
            String[] resourceNames = uriStr.split("/");
            Resource resource = new Resource();
            resource.setVideo(job.getVideo());
            resource.setType(job.getType());
            resource.setGroupId(job.getGroupId());
            resource.setResourceName(resourceNames[resourceNames.length - 1].split("\\.")[0]);
            resource.setResourceKey(uriStr);
            uploadDao.save(resource);
        }
    }

    void hls(Job job, JsonNode result) {
        JsonNode filePaths = result.get("filePaths");
        String resourceType = result.get("dynamicRange").asText().toLowerCase();
        Iterator<JsonNode> resolutionNames = result.get("resolutionInfo").iterator();
        Iterator<JsonNode> keys = result.get("keys").elements();
        for (JsonNode uri : filePaths) {
            String uriStr = uri.asText();
            if (!uriStr.contains("m3u8")) {
                break;
            }
            String resolutionName = resolutionNames.next().get("name").asText();
            Resource resource = new Resource();
            resource.setVideo(job.getVideo());
            resource.setType(job.getType());
            resource.setGroupId(job.getGroupId());
            resource.setResourceName(resolutionName);
            resource.setResourceKey(uriStr);
            resource.setDynamicRange(resourceType);
            uploadDao.save(resource);

            Resource keyResource = new Resource();
            keyResource.setVideo(job.getVideo());
            keyResource.setType("KEY");
            keyResource.setGroupId(job.getGroupId());
            keyResource.setResourceName(resolutionName);
            keyResource.setResourceKey(keys.next().asText());
            keyResource.setDynamicRange(resourceType);
            keyResource.setVideoCodec(job.getType().split("_")[1].toLowerCase());
            uploadDao.save(keyResource);
        }
    }

    void transcode(Job job, JsonNode result) {
        JsonNode filePaths = result.get("filePaths");
        JsonNode resourceType = result.get("dynamicRange");
        Iterator<JsonNode> resolutionNames = result.get("resolutionInfo").iterator();
        filePaths.forEach{uri ->
            String uriStr = uri.asText();
            Resource resource = new Resource();
            resource.setVideo(job.getVideo());
            resource.setType(job.getType());
            resource.setGroupId(job.getGroupId());
            resource.setResourceName(resolutionNames.next().get("name").asText());
            resource.setResourceKey(uriStr);
            resource.setDynamicRange(resourceType.isNull() ? null : resourceType.asText().toLowerCase());
            uploadDao.save(resource);
        }
    }

    void deleteOldResources(String oldGroupId) {
        uploadDao.deleteResource(oldGroupId);
    }

    void checkVideoState(Job job, Video video) {
        boolean screenshot = false;
        boolean hls = false;
        List<Job> jobs = uploadDao.getJobs(job.getGroupId());
        for (Job videoJob : jobs) {
            if (videoJob.isSuccess()) {
                if ("SCREENSHOT".equals(videoJob.getType())) {
                    screenshot = true;
                } else if ("HLS_AVC".equals(videoJob.getType()) || "HLS_HEVC".equals(videoJob.getType()) || "HLS_VP9".equals(videoJob.getType())) {
                    hls = true;
                }
            }
        }
        if (screenshot && hls && video.getState() < 4) {
            //设置为待审核
            video.setState(3);
        }
    }
}